/**
 * Framework for batch testing user profile models
 * Final project by Sergey Nepomnyachiy and Julia Polchin
 * Supervisor: Tsvi Kuflik
 *
 */

package core.generators;

import interfaces.GeneratorInterface;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Scanner;
import java.util.Vector;

import core.environment.Defines;
import core.environment.fVector;

/***
 * CSV File Loader Class Performs basic operations on CSV files: loading,
 * parsing and storing the CSV file's data into a List object. The format of csv
 * file is as following:
 * 
 * <type1>,<type2>,<type3>... data01,data02,data03 data11,data12,data13
 * 
 * For example: int,double,string 1,3.145,test
 */

public class CSVGenerator implements GeneratorInterface<fVector> {
/**
 * the separator of CSV
 */
	final private static String delimiter = ", |,"; // 
/**
 *  the given file
 */
	private File file; //
	/**
	 * list of lines (each is array)
	 */
	private Collection<fVector> matrix; // 

	/**
	 * Constructor with a File object given
	 * 
	 * @param file
	 *            The file to load
	 */
	public CSVGenerator(File file) {
		this.file = file;
		this.matrix = new Vector<fVector>();
	}

	/**
	 * Parse single CS-string and get type names
	 * 
	 * @param lineOfFile
	 *            Coma separated line
	 * @return array of type names
	 */
	private Object[] getTypes(String lineOfFile) {
		Vector<String> result = new Vector<String>();
		Scanner countScanner = new Scanner(lineOfFile);
		countScanner.useDelimiter(delimiter);
		while (countScanner.hasNext()) {
			String s = countScanner.next();
			assert (s.equals("int") || s.equals("double") || s.equals("string"));
			result.add(s);
		}

		return result.toArray();

	}

	/**
	 * Deserializes object from string The types allowed are - string,int,double
	 * For any other type name null is returned. Upon unparseable object we
	 * quit.
	 * 
	 * @param type
	 *            Name of the type
	 * @param obj
	 *            Serialized object string
	 * @return Object constructed form 'obj' - either string, int or double
	 */
	@SuppressWarnings( { "nls", "boxing" })
	private Object parseObject(String type, String obj) {
		try {
			if ("string".equals(type))
				return obj;
			if ("int".equals(type))
				return Integer.parseInt(obj);
			if ("double".equals(type))
				return Double.parseDouble(obj);
		} catch (Exception e) {
			Defines.printException("Type check failed on csv generator in: "
					+ obj + " of " + type);
		}
		return null;
	}

	/**
	 * Performs the actual parsing of the file Will run in O(lines of file)
	 * 
	 * @return Matrix with data from CSV
	 */
	@SuppressWarnings("nls")
	@Override
	public Collection<fVector> getVector()

	{
		InputStream CSVstream;
		Scanner lineScanner;
		Scanner wordScanner;
		String lineOfFile = "";
		fVector row;

		Object[] types = null;

		try {
			CSVstream = new FileInputStream(this.file);
		} catch (FileNotFoundException e) {
			Defines.printException("in csvGenerator - " + e.getMessage());
			return this.matrix;
		}

		lineScanner = new Scanner(CSVstream);
		lineScanner.useDelimiter(System.getProperty("line.separator")
				+ "|\n|\r\n"); // native/Unix/Win form

		if (lineScanner.hasNext()) { // has at least one line
			lineOfFile = lineScanner.next(); // read title line
			types = getTypes(lineOfFile);
		} else
			return this.matrix;

		lineOfFile = lineScanner.next();

		while (!lineOfFile.isEmpty()) { // for all lines do:
			row = new fVector();
			int columns = 0;
			wordScanner = new Scanner(lineOfFile);
			wordScanner.useDelimiter(delimiter);
			// for each word in this line:
			while (wordScanner.hasNext())
				row.add(parseObject((String) types[columns++], wordScanner
						.next()));

			this.matrix.add(row);

			if (lineScanner.hasNext())
				lineOfFile = lineScanner.next();
			else
				break;
		}

		return this.matrix;
	}

	@Override
	public fVector getScalar() {
		return null;
	}

	@Override
	public boolean is2D() {
		return true;
	}

}
